#pragma once

#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "../comm/Util.hpp"
#include "../comm/Log.hpp"

namespace ns_compiler
{
    //  引入路径拼接功能
    using namespace ns_util;

    //  引入日志
    using namespace ns_log;

    class Compiler
    {
    public:
        Compiler()
        {
        }

        //  用于编译指定文件
        static bool Compile(const std::string &file_name)
        {
            pid_t id = fork();
            if (id < 0)
            {
                LOG(ERROR, "内部错误:编译时创建子进程失败\n");
                return false;
            }
            else if (id == 0)
            {
                //  子进程

                umask(0);
                int comperr_fd = open(PathUtil::CompilerErr(file_name).c_str(), O_CREAT | O_WRONLY, 0644);
                if (comperr_fd < 0)
                {
                    LOG(WARNING, "内部错误:编译时打开compiler_error文件失败\n");
                    exit(2);
                }

                //  将标准错误重定向到comperr_fd
                dup2(comperr_fd, 2);

                //  让子进程调用编译器，完成对代码的编译操作
                int n = execlp("g++", "g++", "-o",
                               PathUtil::Exe(file_name).c_str(),
                               PathUtil::Src(file_name).c_str(),
                               "-std=c++11", 
                                "-D", "COMPILER_ONLINE",
                                nullptr);

                LOG(ERROR, "内部错误:编译时执行execlp失败\n");
                exit(1);
            }
            else
            {
                //  父进程

                waitpid(id, nullptr, 0); //  等待子进程结束

                if (FileUtil::IsFileExists(PathUtil::Exe(file_name))) //  如果生成了可执行文件，则认为编译成功
                {
                    LOG(INFO, " [%s] 编译成功\n", PathUtil::Src(file_name).c_str());
                    return true;
                }
            }
            LOG(ERROR, "编译失败，没有形成可执行文件\n");
            return false;
        }
        ~Compiler()
        {
        }

    private:
    };

}